/***************************************************************************
 *	@file 		fetion_config.cpp
 *	@Author 	baoyu0125@gmail.com
 *	@create		2011-7
 *	@version 	1.0.0
 *	@brief		source file about configuration, contain operation of 
 *				configuration
 *	@modify		none
 ***************************************************************************/

#include "fetion_util.h"
#include "fetion_config.h"
#include "fetion_contact.h"
#include "fetion_user.h"
#include "fetion_exception.h"
#include "fetion_database.h"

Config::Config(const std::string &globalPath) : global_path(globalPath), user_list(NULL)
{
	int e;
	try {
		e = mkdir(globalPath.c_str(), S_IRWXU|S_IRWXO|S_IRWXG);
		if(e && access(globalPath.c_str(), R_OK|W_OK)) {
			throw fetionException("global path can't be create, read or write.");
		}

		std::string ul_db_path = global_path + "/data.db";
		user_list = new userlist(ul_db_path);
		if(NULL == user_list) {
			throw fetionException("memory allocation of userlist failed.");
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "create config failed : %s.", ex.what());
	}
}

Config::~Config()
{
	if(NULL != user_list) {
		delete user_list; user_list = NULL;
	}
}

int Config::init(const std::string &userId)
{
	int e;
	int ret = FUNC_SUCC;

	try {	// write exception mode just for code expending
		userPath = global_path + "/" + userId;
		e = mkdir(userPath.c_str(), S_IRWXU|S_IRWXO|S_IRWXG);
		if(e && access(userPath.c_str(), R_OK|W_OK)) {
			throw fetionException("user configuration path %s can't be create, read or write.", userPath.c_str());
		}
		db_path = userPath + "/data.db";
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "user configuration initial failed : %s", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}

int Config::create_table_in_db()
{
	int ret = FUNC_SUCC;
	std::string SQL;
	SQLiteWrapper sqlite;
	
	try {
		if(!sqlite.Open(db_path)) {
			throw fetionException("can't open global local database.");
		}
		SQL = "create table config (autoLogin,	sendButton, autoReply,"
			  "savePasswd,autoDisplayMsg,onlineNotify,sysNodify,"
			  "autoReplayMsg,configServersVersion,configParametersVersion,"
			  "configHintsVersion,portraitServerName,portraitServerPath,"
			  "sipcProxyIP,sipcProxyPort);";
		
		if(!sqlite.DirectStatement(SQL)) {
			throw fetionException("excute SQL statement failed.");
		}
	}
	catch(char *c) {
		WRITE_LOG(LOG_DEBUG, "create table config in database failed : %s", c);
		ret = FUNC_FAIL;
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "create table config in database failed : %s", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}


int Config::load_from_database()
{
	int ret = FUNC_SUCC;
	SQLiteStatement *stmt = NULL;
	SQLiteWrapper sqlite;

	try {
		if(!sqlite.Open(db_path)) {
			throw fetionException("can't open global local database.");
		}

		stmt = sqlite.Statement("select * from config;");
		if(NULL == stmt) {
			if(FUNC_SUCC != create_table_in_db()) {
				throw fetionException("can't create table config in database.");
			}
			stmt = sqlite.Statement("select * from config;");
			if(NULL == stmt) {
				throw fetionException("generate SQL statement object failed.");
			}
		}
		if(!stmt->NextRow()) {
			return ret;
		}
		
		autoLogin				= stmt->ValueInt(0);
		sendButton				= stmt->ValueInt(1);
		autoReply				= stmt->ValueInt(2);
		savePasswd				= stmt->ValueInt(3);
		autoDisplayMsg			= stmt->ValueInt(4);
		onlineNotify			= stmt->ValueInt(5);
		sysNotify				= stmt->ValueInt(6);
		autoReplayMsg			= stmt->ValueString(7);
		configServersVersion	= stmt->ValueString(8);
		configParametersVersion	= stmt->ValueString(9);
		configHintsVersion		= stmt->ValueString(10);
		portraitServerName		= stmt->ValueString(11);
		portraitServerPath		= stmt->ValueString(12);
		sipcProxyIP				= stmt->ValueString(13);
		sipcProxyPort			= stmt->ValueInt(14);

		if(stmt->NextRow()) {
			throw fetionException("query more than one result in table config.");
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "load configuration from database failed : %s", ex.what());
		ret = FUNC_FAIL;
	}
	if(NULL != stmt) {
		delete stmt; stmt = NULL;
	}
	if(FUNC_SUCC == ret) {
		WRITE_LOG(LOG_TRACE, "load configuration from database.");
	}
	return ret;
}

int Config::generate_config_body(std::string &body)
{
	int ret = FUNC_SUCC;
	xmlParser parser;
	xmlpp::Document *doc;
	xmlpp::Node *pNode;
	xmlpp::Element *elem = NULL;
	std::string buf = "<config></config>";
	currentUser *user = currentUser::get_instance();

	try {
		parser.parse_memory(buf);
		doc = parser.get_document();
		pNode = doc->get_root_node();
		elem = pNode->add_child("user");
		if(NULL == elem) {
			throw fetionException("add child node user to xml body failed.");
		}
		elem->set_attribute("mobile-no", user->mobileno);

		elem = pNode->add_child("client");
		if(NULL == elem) {
			throw fetionException("add child node client to xml body failed.");
		}
		elem->set_attribute("type", "PC");
		elem->set_attribute("version", "4.0.2510");
		elem->set_attribute("platform", "W5.1");

		elem = pNode->add_child("servers");
		if(NULL == elem) {
			throw fetionException("add child node servers to xml body failed.");
		}
		elem->set_attribute("version", configServersVersion);

		elem = pNode->add_child("parameters");
		if(NULL == elem) {
			throw fetionException("add child node parameters to xml body failed.");
		}
		elem->set_attribute("version", configParametersVersion);

		elem = pNode->add_child("hints");
		if(NULL == elem) {
			throw fetionException("add child node hints to xml body failed.");
		}
		elem->set_attribute("version", configHintsVersion);
		body = doc->write_to_string();
		xml_convert(body);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "generate config body failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;	
}

int Config::parse_config_xml(Glib::ustring &xml)
{
	int ret = FUNC_SUCC;
	xmlParser parser;
	xmlpp::Document *doc;
	xmlpp::Node *node, *cnode;
	const xmlpp::Element *elem;
	std::string tmp;
	std::string::size_type pos;
	SQLiteWrapper sqlite; 

	try {
		parser.parse_memory(xml);
		doc = parser.get_document();
		node = doc->get_root_node();

		cnode = parser.get_specify_node(node, "servers");
		if(NULL != cnode) {
			elem = dynamic_cast<xmlpp::Element*>(cnode);
			configServersVersion = elem->get_attribute_value("version");
		}
		
		cnode = parser.get_specify_node(node, "parameters");
		if(NULL != cnode) {
			elem = dynamic_cast<xmlpp::Element*>(cnode);
			configParametersVersion = elem->get_attribute_value("version");
		}
		
		cnode = parser.get_specify_node(node, "hints");
		if(NULL != cnode) {
			elem = dynamic_cast<xmlpp::Element*>(cnode);
			configHintsVersion = elem->get_attribute_value("version");
		}
		
		cnode = parser.get_specify_node(node, "sipc-proxy");
		if(NULL != cnode) {
			tmp = parser.xml_get_text_content(cnode);
			if(tmp.empty()) {
				throw fetionException("get sipc proxy server address failed.");
			}
			pos = tmp.find(":", 0);
			sipcProxyIP = tmp.substr(0, pos);
			tmp = tmp.substr(pos+1, tmp.length()-pos-1);
			sipcProxyPort = atoi(tmp.c_str());
		}
		
		cnode = parser.get_specify_node(node, "get-uri");
		if(NULL != cnode) {
			tmp = parser.xml_get_text_content(cnode);
			if(!tmp.empty()) {
				pos = tmp.find("//", 0);
				portraitServerName = tmp.substr(pos+2, tmp.length()-pos-2);
				pos = portraitServerName.find("/", 0);
				portraitServerPath = portraitServerName.substr(pos+1, portraitServerName.length()-pos-1);
				portraitServerName = portraitServerName.substr(0, pos);
			}
		}

		cnode = parser.get_specify_node(node, "addbuddy-phrases");
		if(NULL != cnode) {
			if(!sqlite.Open(db_path)) {
				throw fetionException("can't open global local database.");
			}
			if(!sqlite.DirectStatement("delete from phrases;")) {
				if(!sqlite.DirectStatement("create table phrases (id,content);")) {
					throw fetionException("create table phrases failed.");
				}
			}
		
			xmlpp::Node::NodeList children_list = cnode->get_children();
			std::string id, content;
			std::stringstream SQL;
			for(xmlpp::Node::NodeList::const_iterator iter = children_list.begin();
					iter != children_list.end(); ++iter) {
				SQL.str("");
				content = parser.xml_get_text_content(*iter);
				elem = dynamic_cast<const xmlpp::Element*>(*iter);
				id = elem->get_attribute_value("id");
				SQL<<"insert into phrases values ("<<id<<", '"<<content<<"');";
				if(!sqlite.DirectStatement(SQL.str())) {
					throw fetionException("get phrases failed.");
				}
			}
		}
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "parse configuration xml failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}

int Config::save_to_database()
{
	int ret = FUNC_SUCC;
	SQLiteWrapper sqlite;
	std::stringstream SQL;
	bool retry = false;

	try {
		if(!sqlite.Open(db_path)) {
			throw fetionException("can't open global local database.");
		}
		do {
			if(!sqlite.DirectStatement("delete from config;")) {
				if(FUNC_SUCC != create_table_in_db()) {
					throw fetionException("create table config failed.");
				}
			}
			
			SQL<<"insert into config values ("
			   <<autoLogin<<","<<sendButton<<","<<autoReply<<","
			   <<savePasswd<<","<<autoDisplayMsg<<","<<onlineNotify<<","
			   <<sysNotify<<",'"<<autoReplayMsg<<"','"<<configServersVersion<<"','"
			   <<configParametersVersion<<"','"<<configHintsVersion<<"','"
			   <<portraitServerName<<"','"<<portraitServerPath<<"','"
			   <<sipcProxyIP<<"',"<<sipcProxyPort<<");";

			if(!sqlite.DirectStatement(SQL.str())) {
				if(!sqlite.DirectStatement("drop table config;")) {
					throw fetionException("drop table config failed.");
				}
				if(retry) {
					throw fetionException("insert to table config failed.");
				} else {
					retry = true;
					continue;
				}
			}
			break;
		}while(true);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "save config to database failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	if(FUNC_SUCC == ret) {
		WRITE_LOG(LOG_TRACE, "save configuration to database.");
	}
	return ret;
}

int Config::get_phrase(const int id, std::string &phrase)
{
	int ret = FUNC_SUCC;
	SQLiteWrapper sqlite;
	SQLiteStatement *stmt = NULL;
	std::stringstream SQL;

	try {
		if(!sqlite.Open(db_path)) {
			throw fetionException("can't open global local database.");
		}
		SQL<<"select * from phrases where id="<<id<<";";
		stmt = sqlite.Statement(SQL.str());
		if(NULL == stmt) {
			throw fetionException("get SQL statement object failed.");
		}
		if(!stmt->NextRow()) {
			throw fetionException("execute SQL statement failed.");
		}
		phrase = stmt->ValueString(1);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "get phrase failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}
